package mt.omid.jscap;

/****
 * Create by Omid Mehdizadeh
 * Copyright 2010, Omid Mehdizadeh
 * Based on JDiameter Open Source Project
 */

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Set;

import org.jdiameter.api.ApplicationId;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.AvpSet;
import org.jdiameter.api.Configuration;
import org.jdiameter.api.IllegalDiameterStateException;
import org.jdiameter.api.InternalException;
import org.jdiameter.api.OverloadException;
import org.jdiameter.api.Peer;
import org.jdiameter.api.PeerStateListener;
import org.jdiameter.api.ResultCode;
import org.jdiameter.api.URI;
import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.IMetaData;
import org.jdiameter.client.api.fsm.IContext;
import org.jdiameter.client.api.fsm.IFsmFactory;
import org.jdiameter.client.api.io.IConnection;
import org.jdiameter.client.api.io.ITransportLayerFactory;
import org.jdiameter.client.api.io.TransportException;
import org.jdiameter.client.api.parser.IMessageParser;
import org.jdiameter.client.impl.controller.PeerImpl;
import org.jdiameter.client.impl.controller.PeerTableImpl;
import org.jdiameter.common.api.concurrent.IConcurrentFactory;
import org.jdiameter.common.api.statistic.IStatisticFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.jdiameter.api.Avp.*;
import static org.jdiameter.api.Message.*;


public class SCAPPeerImpl extends PeerImpl
{

    private static final Logger log = LoggerFactory.getLogger( PeerImpl.class );


    public SCAPPeerImpl( PeerTableImpl arg0, int arg1, URI arg2, String arg3, String arg4,
			 IMetaData arg5, Configuration arg6, Configuration arg7, IFsmFactory arg8,
			 ITransportLayerFactory arg9, IStatisticFactory arg10,
			 IConcurrentFactory arg11, IMessageParser arg12 ) throws InternalException,
	    TransportException
    {
	super( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 );
    }

    protected SCAPPeerImpl( PeerTableImpl arg0, int arg1, URI arg2, String arg3, String arg4,
			    IMetaData arg5, Configuration arg6, Configuration arg7,
			    IFsmFactory arg8, ITransportLayerFactory arg9, IMessageParser arg10,
			    IStatisticFactory arg11, IConcurrentFactory arg12, IConnection arg13 )
	    throws InternalException, TransportException
    {
	super( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
		arg13 );
    }

    public IContext getContext()
    {
	return new SCAPActionContext();
    }

    protected void fillIPAddressTable( IMessage message )
    {
	AvpSet avps = message.getAvps().getAvps( HOST_IP_ADDRESS );
	if( avps != null )
	{
	    ArrayList<InetAddress> t = new ArrayList<InetAddress>();
	    for( int i = 0; i < avps.size(); i++ )
		try
		{
		    t.add( avps.getAvpByIndex( i ).getAddress() );
		}
		catch( AvpDataException e )
		{
		    log.debug( "Can not get ip address from HOST_IP_ADDRESS avp" );
		}
	    addresses = t.toArray( new InetAddress [ t.size() ] );
	}
    }


    protected class SCAPActionContext extends ActionContext
    {

	protected void addAppId( ApplicationId appId, IMessage message )
	{
	    if( appId.getVendorId() == 0 )
	    {
		if( appId.getAuthAppId() != 0 )
		{
		    message.getAvps().addAvp( AUTH_APPLICATION_ID, appId.getAuthAppId(), true,
			    false, true );
		}
		else if( appId.getAcctAppId() != 0 )
		{
		    message.getAvps().addAvp( ACCT_APPLICATION_ID, appId.getAcctAppId(), true,
			    false, true );
		}
	    }
	    else
	    {
		// message.getAvps().addAvp( SUPPORTED_VENDOR_ID, appId.getVendorId(), true, false, true );
		AvpSet vendorApp = message.getAvps().addGroupedAvp( VENDOR_SPECIFIC_APPLICATION_ID,
			true, false );
		vendorApp.addAvp( VENDOR_ID, appId.getVendorId(), true, false, true );
		if( appId.getAuthAppId() != 0 )
		    vendorApp.addAvp( AUTH_APPLICATION_ID, appId.getAuthAppId(), true, false, true );
		if( appId.getAcctAppId() != 0 )
		    vendorApp.addAvp( ACCT_APPLICATION_ID, appId.getAcctAppId(), true, false, true );
	    }
	}

	public void sendCerMessage() throws TransportException, OverloadException
	{
	    log.debug( "Send SCAPv1 CER message" );
	    IMessage message = parser.createEmptyMessage( CAPABILITIES_EXCHANGE_REQUEST, 0 );
	    message.setRequest( true );
	    message.setHopByHopIdentifier( getHopByHopIdentifier() );

	    message.getAvps().addAvp( ORIGIN_HOST, metaData.getLocalPeer().getUri().toString(),
		    true, false, true );
	    message.getAvps().addAvp( ORIGIN_REALM, metaData.getLocalPeer().getRealmName(), true,
		    false, true );
	    for( InetAddress ia : metaData.getLocalPeer().getIPAddresses() )
		message.getAvps().addAvp( HOST_IP_ADDRESS, ia.getAddress(), true, false );
	    message.getAvps().addAvp( VENDOR_ID, metaData.getLocalPeer().getVendorId(), true,
		    false, true );
	    message.getAvps()
		    .addAvp( PRODUCT_NAME, metaData.getLocalPeer().getProductName(), false );
	    for( ApplicationId appId : metaData.getLocalPeer().getCommonApplications() )
		addAppId( appId, message );
	    //	    message.getAvps().addAvp( FIRMWARE_REVISION, metaData.getLocalPeer().getFirmware(),
	    //		    true );
	    message.getAvps().addAvp( ORIGIN_STATE_ID, metaData.getLocalHostStateId(), true, false,
		    true );
	    sendMessage( message );
	}

	public void sendDprMessage( int disconnectCause ) throws TransportException,
		OverloadException
	{
	    log.debug( "Send SCAPv1 DPR message" );
	    IMessage message = parser.createEmptyMessage( DISCONNECT_PEER_REQUEST, 0 );
	    message.setRequest( true );
	    message.setHopByHopIdentifier( getHopByHopIdentifier() );
	    message.getAvps().addAvp( ORIGIN_HOST, metaData.getLocalPeer().getUri().toString(),
		    true, false, true );
	    message.getAvps().addAvp( ORIGIN_REALM, metaData.getLocalPeer().getRealmName(), true,
		    false, true );
	    if( disconnectCause == 2001 )
		message.getAvps().addAvp( DISCONNECT_CAUSE,
			DisconnectCause.DO_NOT_WANT_TO_TALK_TO_YOU, true, false );
	    else
		message.getAvps().addAvp( DISCONNECT_CAUSE, disconnectCause, true, false );
	    sendMessage( message );
	}

	public void sendDpaMessage( IMessage dpr, int resultCode, String errorMessage )
		throws TransportException, OverloadException
	{
	    log.debug( "Send SCAPv1 DPA message" );
	    IMessage message = parser.createEmptyMessage( dpr );
	    message.setRequest( false );
	    message.setHopByHopIdentifier( dpr.getHopByHopIdentifier() );
	    message.setEndToEndIdentifier( dpr.getEndToEndIdentifier() );
	    message.getAvps().addAvp( RESULT_CODE, resultCode, true, false, true );
	    if( errorMessage != null )
		message.getAvps().addAvp( ERROR_MESSAGE, errorMessage, false );
	    // Replace Origin Host with SCAP compatible
	    message.getAvps().removeAvp( ORIGIN_HOST );
	    message.getAvps().addAvp( ORIGIN_HOST, metaData.getLocalPeer().getUri().toString(),
		    true, false, true );
	    sendMessage( message );
	}

	public void sendDwrMessage() throws TransportException, OverloadException
	{
	    log.debug( "Send SCAPv1 DWR message" );
	    IMessage message = parser.createEmptyMessage( DEVICE_WATCHDOG_REQUEST, 0 );
	    message.setRequest( true );
	    message.setHopByHopIdentifier( getHopByHopIdentifier() );
	    // Set content
	    message.getAvps().addAvp( ORIGIN_HOST, metaData.getLocalPeer().getUri().toString(),
		    true, false, true );
	    message.getAvps().addAvp( ORIGIN_REALM, metaData.getLocalPeer().getRealmName(), true,
		    false, true );
	    message.getAvps().addAvp( ORIGIN_STATE_ID, metaData.getLocalHostStateId(), true, false,
		    true );
	    // Remove trash avp
	    message.getAvps().removeAvp( DESTINATION_HOST );
	    message.getAvps().removeAvp( DESTINATION_REALM );
	    // Send
	    sendMessage( message );
	}

	public void sendDwaMessage( IMessage dwr, int resultCode, String errorMessage )
		throws TransportException, OverloadException
	{
	    log.debug( "Send SCAPv1 DWA message" );
	    IMessage message = parser.createEmptyMessage( dwr );
	    message.setRequest( false );
	    message.setHopByHopIdentifier( dwr.getHopByHopIdentifier() );
	    message.setEndToEndIdentifier( dwr.getEndToEndIdentifier() );
	    // Set content
	    message.getAvps().addAvp( RESULT_CODE, resultCode, true, false, true );
	    if( errorMessage != null )
		message.getAvps().addAvp( ERROR_MESSAGE, errorMessage, false );
	    // Replace Origin Host with SCAP compatible
	    message.getAvps().removeAvp( ORIGIN_HOST );
	    message.getAvps().addAvp( ORIGIN_HOST, metaData.getLocalPeer().getUri().toString(),
		    true, false, true );
	    // Remove trash avp 
	    message.getAvps().removeAvp( DESTINATION_HOST );
	    message.getAvps().removeAvp( DESTINATION_REALM );

	    sendMessage( message );
	}

	public boolean sendMessage( IMessage message ) throws TransportException, OverloadException
	{
	    // Check message
	    if( message.isTimeOut() )
	    {
		log.debug( "Message: " + message + " skipped (timeout)" );
		return false;
	    }
	    if( message.getState() == IMessage.STATE_SENT )
	    {
		log.debug( "Message: " + message + " already send" );
		return false;
	    }
	    // Remove destionation information from answer messages
	    if( !message.isRequest() )
	    {
		message.getAvps().removeAvp( DESTINATION_HOST );
		message.getAvps().removeAvp( DESTINATION_REALM );
	    }
	    // Send to network
	    message.setState( IMessage.STATE_SENT );
	    connection.sendMessage( message );
	    log.debug( "Send SCAPv1 message " + message + " to peer " + SCAPPeerImpl.this.getUri() );

	    return true;
	}

    }

}
